#include "cola.h"
#include <stdlib.h>

/* ******************************************************************
 *                DEFINICION DE LOS TIPOS DE DATOS
 * *****************************************************************/

//Definicion de la estructura nodo y del tipo nodo_t.
typedef struct nodo{

	void* dato;

	struct nodo* prox;

}nodo_t;



//Definicion de la estructura cola.
struct cola{

	struct nodo* prim;

	struct nodo* ult;

	size_t len;

};

/* ******************************************************************
 *                    PRIMITIVAS DE LA COLA
 * *****************************************************************/

//Devuelve un nodo con un puntero a un valor pasado por parametro.
//El puntero a su proximo es inicializado como un puntero nulo.
//Devuelve NULL si no se pudo crear el nodo.
nodo_t* crear_nodo(void* value){
	
	if(!value) return NULL;

	nodo_t* nodo=(nodo_t*)malloc(sizeof(nodo_t));

	if(!nodo) return NULL;

	nodo->dato=value;

	nodo->prox=NULL;

	return nodo;
}

//Devuelve una cola de tamanio, cuyos punteros al ultimo nodo y al primer
//nodo estan inicializados en nulo. Devuelve NULL si no se pudo crear
//la cola.
cola_t* cola_crear(void){

	cola_t* cola=(cola_t*)malloc(sizeof(cola_t));

	if(!cola) return NULL;

	cola->len=0;

	cola->prim=NULL;

	cola->ult=NULL;

	return cola;
}

//Destruye la cola pasada por parametro y los valores almacenados en sus nodos.
//Para destruir los valores recibe una funcion por parametro.
void cola_destruir(cola_t* cola, void destruir_dato(void*)){
	
	//Se toma el primer nodo de la cola.
	nodo_t* nodo=cola->prim;
	
	nodo_t*	aux;
	
	//Mientras exista un primero de la cola, se siguen sacando de la misma.
	while(nodo){
		
		//Destruye el dato del nodo si se le paso una funcion por parametro y si hay dato.
		if (destruir_dato /*&& (nodo->dato)*/) destruir_dato(nodo->dato);
		
		//Guarda la referencia al siguiente nodo.
		aux=nodo->prox;
		
		//Libera el nodo sobre el que se estaba trabajando.
		free(nodo);
		
		//El nuevo nodo es el siguiente al que se empezo a tratar.
		nodo=aux;
		
		//Disminuye el tamanio de la cola para mantere el invariante.
		(cola->len)--;
	}
	
	free(cola);
}

//Devuelve true si la cola esta vacia o false si no lo esta.
bool cola_esta_vacia(const cola_t* cola){

	return ( (cola->len==0) && (cola!=NULL) )? true:false;
}

//Encola un puntero a un valor dentro de una cola. Para esto se crea
//un nodo al cual se le asigna el puntero al valor. El nodo se encola
//en la cola pasada por parametro. Devuelve true si se pudo encolar, o
//false en su defecto.
bool cola_encolar(cola_t* cola, void* valor){
	
	if( (!cola) || (!valor) ) return false;

	nodo_t* nodo=crear_nodo(valor);

	//Excepcion de cuando la cola esta vacia, lo encolado es el primer y ultimo nodo a la vez.
	if(cola_esta_vacia(cola)) (cola->prim)=(cola->ult)=nodo;

	else (cola->ult)->prox=nodo;
	
	cola->ult=nodo;

	(cola->len)++;

	return true;
}

//Devuelve un puntero al valor que esta almacenado en el primer nodo de la
//cola pasada por parametro. No altera la cola.
void* cola_ver_primero(const cola_t* cola){
	
	if( (!cola) || (cola_esta_vacia(cola)) ) return NULL;
	
	return (cola->prim)->dato;
}

//Desencola un nodo de la cola pasado por parametro y devuelve el puntero
//que estaba almacenado en el nodo.
void* cola_desencolar(cola_t* cola){
	
	if( (!cola) || (cola_esta_vacia(cola)) ) return NULL;
	
	nodo_t* primero=cola->prim;
	
	nodo_t* aux=primero->prox;
	
	void* valor=primero->dato;
	
	free(primero);
	
	if(!aux) (cola->prim)=(cola->ult)=NULL;
	
	else (cola->prim)=aux;
	
	(cola->len)--;
	
	return valor;
}

size_t cola_largo(cola_t* cola){
	
	if(!cola) return -1;
	
	return cola->len;
	
}


